/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dailydev.wave.robot;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.dailydev.wave.robot.context.IEventContext;

import com.google.wave.api.Event;
import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.Wavelet;

/**
 * @author Roman Bosak, DailyDev.org
 */
public class RobotHandlerAdapter implements IRobotHandlerAdapter {

    private static Logger logger = Logger.getLogger(RobotHandlerAdapter.class.getName());

    /**
     * Method implements reflection call of <code>handlerMethod</code> method on
     * <code>handler</code> object. The method then realizes robot's
     * functionality.
     * 
     * @param handler
     *            instance of handler object
     * @param handlerMethod
     *            method to be called using reflection on <code>handler</code>
     *            object
     * @param bundle
     *            bundle of the wave
     * @param event
     *            {@link Event} which should be handled
     * @param context
     *            event handling context optionally filled by filters
     * 
     * @throws RobotException
     *             can be thrown in several cases: <li>target method has wrong
     *             signature</li><li>this method does not have sufficient
     *             privileges to call the method</li><li>target method call
     *             throws exception</li>
     */
    @Override
    public void handleEvent(Object handler, Method handlerMethod, RobotMessageBundle bundle, Event event,
            IEventContext context) throws RobotException {
        Class<?>[] parameterTypes = handlerMethod.getParameterTypes();

        if (parameterTypes.length > 4) {
            throw new RobotException("Robot handler method " + handlerMethod.toGenericString()
                    + " does not have required signature.");
        }

        Object[] params = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            if (parameterTypes[i] == RobotMessageBundle.class) {
                params[i] = bundle;
            } else if (parameterTypes[i] == Event.class) {
                params[i] = event;
            } else if (parameterTypes[i] == Wavelet.class) {
                params[i] = event.getWavelet();
            } else if (parameterTypes[i] == IEventContext.class) {
                params[i] = context;
            } else {
                throw new RobotException("Robot handler method " + handlerMethod.toGenericString()
                        + " does not have required signature. Parameters of type '" + parameterTypes[i]
                        + " are not supported.");
            }
        }

        try {
            if (logger.isLoggable(Level.FINEST)) {
                logger.finest("\tRobot handler method call> " + handlerMethod.toGenericString()
                        + " using following parameters> " + Arrays.toString(params));
            }
            handlerMethod.invoke(handler, params);
        } catch (IllegalArgumentException e) {
            throw new RobotException("Robot handler method " + handlerMethod.toGenericString()
                    + " does not have required signature.", e);
        } catch (IllegalAccessException e) {
            throw new RobotException("Robot handler method call denied (" + handlerMethod.toGenericString() + ")", e);
        } catch (InvocationTargetException e) {
            throw new RobotException("Robot handler method " + handlerMethod.toGenericString() + " thrown "
                    + e.getTargetException(), e);
        }
    }
}
